# -*- coding: utf-8 -*-

""" ModelFormatter - recursively render model's attributes in JSON templates.

>>> from jsontemplate import Template
>>> options = {
...   'more_formatters': {
...     'model': ModelFormatter,
...   }.get,
... }

>>> thing = {'name': 'Thing', 'serious': 'ownage'}

>>> t = Template("This is the thing: {thing|model}", **options)
>>> print t.expand({'thing': thing})
This is the thing: <pre>
<strong>dict-object</strong>
serious: ownage
name: Thing
</pre>
"""

import cgi

def _typeof(model):
    """ 
    >>> _typeof([1,2,3])
    'object'
    >>> _typeof('foo')
    'atom'
    >>> _typeof(None)
    'atom'
    """
    if (isinstance(model, list) or isinstance(model, tuple) or isinstance(model, dict) or hasattr(model, "__dict__")):
        return "object"
    return "atom"

def _getattr(model, x):
    """
    >>> _getattr(42, "x")
    >>> _getattr(cgi, "escape").func_name
    'escape'
    >>> _getattr([1,2,3], 0)
    1
    """
    try: return getattr(model, x, None) 
    except:
        try: return model[x]
        except: return None

def _getattributes(model):
    """
    >>> _getattributes([1,2,3])
    [(0, 1), (1, 2), (2, 3)]
    >>> _getattributes({'a': 1, 'b': 2})
    [('a', 1), ('b', 2)]
    >>> class Foo: a=1
    >>> _getattributes(Foo)
    [('a', 1)]
    """
    d = _getattr(model, "__dict__")
    if d:
        return [(k,v) for k,v in d.items() if not k.startswith("__")]
    if isinstance(model, dict):
        return model.items()
    iter = _getattr(model, '__iter__')
    if iter:
        return list(enumerate(iter()))
    return []
    
def render(model, level):
    """
    >>> render(51, "")
    '51'
    >>> print render([1,2,3], "")
    <pre>
    <strong>list-object</strong>
    0: 1
    1: 2
    2: 3
    </pre>
    """
    s = ""
    if _typeof(model) != "object":
        s += cgi.escape(str(model))
        return s
    s = "\n"
    meta = _getattr(model, "_meta")
    klass = _getattr(model, "__class__")
    name = _getattr(meta, "name") or _getattr(klass, "__name__") and _getattr(klass, "__name__").lower() 
    id = _getattr(meta, "id") or _getattr(model, "article_id") or "object"
    if name:
        s += "%s<strong>%s-%s</strong>\n" % (level, name, id)
    for key, val in _getattributes(model):
        val = render(val, level+"  ")
        s += "%s%s: %s\n" % (level, key, val)
    return level == "" and "<pre>%s</pre>"%s or s

def ModelFormatter(model):
    return render(model, "")


if __name__ == "__main__":
    import doctest    
    doctest.testmod()


__test__ = dict(

  test = """

>>> from jsontemplate import Template
>>> options = {
...   'more_formatters': {
...     'model': ModelFormatter,
...   }.get,
... }

>>> thing = {'name': 'Thing', 'serious': 'ownage'}
>>> t = Template("This is the thing:<br />{thing|model}", **options)
>>> from BeautifulSoup import BeautifulSoup as Soup
  
>>> soup = Soup(t.expand({'thing': thing}))
>>> soup.strong.text
u'dict-object'
>>> print soup.pre.contents[-1].strip()
serious: ownage
name: Thing

>>> class Thing:
...   class _meta:
...     name = "thing"
...     id = "42"

>>> soup = Soup(t.expand({'thing': Thing}))
>>> soup.strong.text
u'thing-42'
>>> print soup.pre.contents[-1].strip()
_meta: 
  name: thing
  id: 42

>>> class K:
...   x = 3
...   y = "foo"

>>> print render(["<script>", dict(a=1001), [1,2,K,4]], "")
<pre>
<strong>list-object</strong>
0: &lt;script&gt;
1: 
  <strong>dict-object</strong>
  a: 1001
<BLANKLINE>
2: 
  <strong>list-object</strong>
  0: 1
  1: 2
  2: 
    y: foo
    x: 3
<BLANKLINE>
  3: 4
<BLANKLINE>
</pre>

>>> print render({"foo": "SCZ"}, "")
TODO: Unicode
""",
  
)